#include<gtk/gtk.h>
#include<gio/gio.h>
#include<string>
#include <napi.h>
#include<gio-unix-2.0/gio/gdesktopappinfo.h>
using namespace Napi;
using std::string;
static std::vector<string> search_path;

void push(const string& path){
    search_path.push_back(path);
}

/*
native method  get_icon
desc:get icon thrrough gtk,gio
param : filename ,icon size
dependency:gtk 3.0
if you only want to get .so library,extract this function and delete eveything relative to napi,
then use "gcc main.c -fPIC -shared -o libiconget.so `pkg-config --cflags --libs gtk+-3.0`" command to compile it.

*/
string get_icon(const string& filename,const int size){
    string result= "unknown";
    //get file from path
    GFile* file = g_file_new_for_path(filename.c_str());
    GError* gerror = 0;
    //query "standard::icon" info from file
    GFileInfo* gfileinfo = g_file_query_info(file,"standard::icon", GFileQueryInfoFlags::G_FILE_QUERY_INFO_NONE,g_cancellable_new(), &gerror);
    if (gerror) {
        g_error_free(gerror);
        gerror = 0;
    }
    if (gfileinfo) {
        //get icon name from gfileinfo
        GIcon* icon = g_file_info_get_icon (gfileinfo);
        if(icon){
            GtkIconTheme* theme = gtk_icon_theme_new();
            //add some ukui paths
            gtk_icon_theme_append_search_path(theme,"/usr/share/icons/ukui/48x48/places");
            gtk_icon_theme_append_search_path(theme,"/usr/share/icons/ukui/48x48/mimetypes");
            gtk_icon_theme_append_search_path(theme,"/usr/share/icons/ukui/48x48/apps");
            gtk_icon_theme_append_search_path(theme,"/usr/share/icons/ukui/48x48/devices");
            GtkIconInfo* info = 0;
            //lookup icon
            info = gtk_icon_theme_lookup_by_gicon (theme,icon,size,GtkIconLookupFlags::GTK_ICON_LOOKUP_NO_SVG);
           if(info){
               //copy result
               result = gtk_icon_info_get_filename(info);
               //free info
               g_object_unref(info);
           }
            g_object_unref(theme);
        }
        //free gfileinfo,file,theme

        g_object_unref(gfileinfo);
        g_object_unref(file);

    }
    return result;
}

string get_desktop_name(const string& dpath,const string& unknow){
    // std::ifstream is(dpath,std::ios_base::in);
    // string iconname;
    // string line;
    // while(getline(is,line)){
    //     if(!strncmp(line.c_str(),"Name=",4)) {
    //         iconname = line.substr(5);
    //         break;
    //     }
    // }
    // is.close();
    // if (iconname!="")return iconname;
    // return unkown;
    GDesktopAppInfo* appinfo = g_desktop_app_info_new_from_filename (dpath.c_str());
    if(!appinfo) return unknow;
    const char* result = g_desktop_app_info_get_string(appinfo,"Name");
    if(result==NULL) return unknow;
    return result;
    
}
string get_type(const string& s){
    GFile* file = g_file_new_for_path(s.c_str());
    GError* gerror = 0;
    //query "standard::icon" info from file
    GFileInfo* gfileinfo = g_file_query_info(file,"standard::icon", GFileQueryInfoFlags::G_FILE_QUERY_INFO_NONE,g_cancellable_new(), &gerror);
    if (gerror) {
        g_error_free(gerror);
        gerror = 0;
        return "";
    }
    GIcon* icon = g_file_info_get_icon (gfileinfo);
    string type = g_themed_icon_get_names ((GThemedIcon*)icon)[0];
    g_object_unref(gfileinfo);
    g_object_unref(file);
    return type;
}
string get_desktop_exec(const string& dpath){
    // std::ifstream is(dpath,std::ios_base::in);
    // string iconname;
    // string line;
    // while(getline(is,line)){
    //     if(!strncmp(line.c_str(),"Exec=",4)) {
    //         iconname = line.substr(5);
    //         break;
    //     }
    // }
    // is.close();
    GDesktopAppInfo* appinfo = g_desktop_app_info_new_from_filename (dpath.c_str());
    const char* re =  g_desktop_app_info_get_string(appinfo,"Exec");
    if(re == NULL) return "";
    return re;
        
}
string get_desktop_icon(const string& dpath,const string& unkpath){
    GDesktopAppInfo* appinfo = g_desktop_app_info_new_from_filename (dpath.c_str());
    const char* c = g_desktop_app_info_get_string(appinfo,"Icon");
    if(c== NULL) return unkpath;
    string iconname = c;
    if(g_path_is_absolute(iconname.c_str())) return iconname;
    GFile* file = 0;
    string cpath;
    for(size_t i=0; i<search_path.size(); i++){
        cpath = search_path[i] + iconname + ".png";
        file = g_file_new_for_path(cpath.c_str());
        if(g_file_query_exists(file,NULL))
        {
            g_object_unref(file);
            return  cpath;
        }
    }
    return unkpath;
}
Array get_recommended_open_way(const CallbackInfo& info){
    Env env = info.Env();
    string path = info[0].As<String>().Utf8Value();
    Array obj_array  = Array::New(env);
    GFile* file = g_file_new_for_path (path.c_str());
    GError* gerror = 0;
    GFileInfo* gfileinfo = g_file_query_info(file,"standard::*", GFileQueryInfoFlags::G_FILE_QUERY_INFO_NONE,g_cancellable_new(), &gerror);
    if(gerror){
        g_error_free(gerror);
        gerror = 0;
        g_object_unref(file);
        return obj_array;
    }
    const char* types = g_file_info_get_content_type (gfileinfo);
    GList* list = g_app_info_get_recommended_for_type (types);
    string exe,name;
    GAppInfo* appinfo = nullptr;
    int i = 0;
    while(list){
        appinfo = static_cast<GAppInfo*>(list->data);
        name = g_app_info_get_name(appinfo);
        exe = g_app_info_get_commandline  (appinfo);
        Object obj = Object::New(env);
        obj.Set("app",name);
        obj.Set("exec",exe);
        obj_array[i] = obj;
        g_object_unref(appinfo);
        list = list->next;
        i++;
    }
    //release object
    g_object_unref(file);
    return obj_array;
}

//transfer  params  from node and call c++ native method get_icon
String call_get_icon(const CallbackInfo& info){
    Env env = info.Env();
    string filename = info[0].As<String>().Utf8Value();
    int size = info[1].ToNumber().Int32Value();
    return String::New(env,get_icon(filename,size));
}
String call_get_desktop_icon(const CallbackInfo& info){
    Env env = info.Env();
    string dpath = info[0].As<String>().Utf8Value();
    string unknow = info[1].As<String>().Utf8Value();
    return String::New(env,get_desktop_icon(dpath,unknow));
}
String call_get_desktop_name(const CallbackInfo& info){
    Env env = info.Env();
    string dpath = info[0].As<String>().Utf8Value();
    string unknow = info[1].As<String>().Utf8Value();
    return String::New(env,get_desktop_name(dpath,unknow));
}
String call_get_desktop_exec(const CallbackInfo& info){
    Env env = info.Env();
    string dpath = info[0].As<String>().Utf8Value();
    return String::New(env,get_desktop_exec(dpath));
}
String call_get_type(const CallbackInfo& info){
    Env env = info.Env();
    string dpath = info[0].As<String>().Utf8Value();
    return String::New(env,get_type(dpath));
}
void call_add_search_path(const CallbackInfo& info){
    string path = info[0].As<String>().Utf8Value();
    push(path);
}
//export method
Napi::Object Init(Env env, Object exports) {
  exports.Set("get_ndesktop_icon", Function::New(env, call_get_icon));
  exports.Set("get_desktop_icon", Function::New(env, call_get_desktop_icon));
  exports.Set("add_seach_path", Function::New(env, call_add_search_path));
  exports.Set("get_desktop_name", Function::New(env, call_get_desktop_name));
  exports.Set("get_desktop_exec", Function::New(env, call_get_desktop_exec));
  exports.Set("get_type", Function::New(env, call_get_type));
  exports.Set("get_recommended_open_way", Function::New(env, get_recommended_open_way));
  return exports;
}
NODE_API_MODULE(addon, Init)